package com.tevintao.marketingsystem.xposted;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.os.RemoteException;

import com.tevintao.marketingsystem.log.LogManager;
import com.tevintao.marketingsystem.services.MockServices;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;


public class HookAudioRecord {
    private static HookAudioRecord ourInstance = new HookAudioRecord();

    public static HookAudioRecord getInstance() {
        return ourInstance;
    }

    public static final String KEY_AUDIO_PATH = "KEY_AUDIO_PATH";


    private HookAudioRecord() {
    }

    private String mPcmPath;
    private onVoiceFinishListener mFinish;

    public void setVoicePcmPath(String path, onVoiceFinishListener listener) {
        mPcmPath = path;
        mFinish = listener;
    }

    public void hookAudioRecord() {
        hookAudioRecordConstructors();
        hookAudioRecordRead();
    }

    InputStream inputStream;

    private void initInputStream() {
        Object pathObj = WxTaskManger.getInstance().getExtra(KEY_AUDIO_PATH);
        if (null != pathObj) {
            LogManager.getInstance().logInfo("initInputStream path =  " + pathObj.toString() + " MockServices.getProcessName = " + MockServices.getProcessName());
            String voidoPath = pathObj.toString();
            File file = new File(voidoPath);
            LogManager.getInstance().logInfo("initInputStream path = " + voidoPath);
            try {
                inputStream = new FileInputStream(file);
                setAudioState(true);
                LogManager.getInstance().logInfo("AudioRecord 创建输入流成功 process name = " + MockServices.getProcessName() + "  getAudioState() = " + getAudioState() + " HookAudioRecord.class.hashCode() =  " + HookAudioRecord.class.hashCode() + " getClassLoader().hashCode() = " + HookAudioRecord.class.getClassLoader().hashCode());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                LogManager.getInstance().logError("AudioRecord FileNotFoundException ");
            }
        }
    }

    public static final String KEY_AUDIO_STATE = "KEY_AUDIO_STATE";

    public static boolean setAudioState(boolean state) {
        if (MockServices.checkClient()) {
            try {
                MockServices.getClient().putBooleanValue(KEY_AUDIO_STATE, state);
            } catch (RemoteException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    public static boolean getAudioState() {
        if (MockServices.checkClient()) {
            try {
                return MockServices.getClient().getBooleanValue(KEY_AUDIO_STATE);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private void hookAudioRecordRead() {
        HookDeviceModule.hookAllMethodByName(AudioRecord.class, "read", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);

                LogManager.getInstance().logInfo("AudioRecord ------------------");
                LogManager.getInstance().logInfo("AudioRecord thisObject = " + param.thisObject + " name = " + param.method);

                for (Object paramObj : param.args) {
                    if (paramObj instanceof short[]) {
                        LogManager.getInstance().logInfo("AudioRecord  paramObj instance short[] " + (((short[]) paramObj).length));
                    } else if (paramObj instanceof byte[]) {
                        LogManager.getInstance().logInfo("AudioRecord  paramObj instance byte[] " + (((byte[]) paramObj).length));
                    } else {
                        LogManager.getInstance().logInfo("AudioRecord paramObj = " + paramObj);
                    }
                }
                if (param.args[0] instanceof byte[] && param.args[1] instanceof Integer && param.args[2] instanceof Integer) {
                    byte[] buffer = (byte[]) param.args[0];
                    int star = (int) param.args[1];
                    int offset = (int) param.args[2];
                    if (inputStream != null) {
                        int len = inputStream.read(buffer, star, offset);
                        LogManager.getInstance().logInfo("读取字节 = " + len);
                        if (-1 == len) {
                            LogManager.getInstance().logInfo("读取完毕");
                            setAudioState(false);
                            try {
                                inputStream.close();
                                inputStream = null;
                            } catch (IOException e) {
                                e.printStackTrace();
                            } finally {
                                inputStream = null;
                            }
                        }
                        LogManager.getInstance().logInfo("AudioRecord read len = " + len);
                    } else {
                        LogManager.getInstance().logInfo("流完毕");
                        if (null != mFinish) {
                            mFinish.onPcmReadFinish();
                        }
                    }
                }
                LogManager.getInstance().logInfo("AudioRecord ------------------");
            }
        });

    }

    private void hookAudioRecordConstructors() {
        XposedBridge.hookAllConstructors(AudioRecord.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                LogManager.getInstance().logInfo("AudioRecord ------------------");
                LogManager.getInstance().logInfo("AudioRecord thisObject = " + param.thisObject + " name = " + param.method);

                for (Object paramObj : param.args) {
                    LogManager.getInstance().logInfo("AudioRecord paramObj = " + paramObj + "   " + (paramObj == null ? " null " : param.getClass().getName()));
                }
                if (param.args.length == 5 && param.args[0] instanceof Integer) {
                    LogManager.getInstance().logInfo("第一次执行");
//                    if (param.args[2] instanceof Integer && (int) param.args[2] == AudioFormat.CHANNEL_CONFIGURATION_MONO) {
//                        LogManager.getInstance().logError("param.args[2] == AudioFormat.CHANNEL_CONFIGURATION_MONO");
//                        param.args[2] = AudioFormat.CHANNEL_OUT_STEREO;
//                    }
                    initInputStream();
                }
                LogManager.getInstance().logInfo("AudioRecord ------------------");

                for (Object paramObj : param.args) {
                    LogManager.getInstance().logInfo("AudioRecord paramObj = " + paramObj + "   " + (paramObj == null ? " null " : param.getClass().getName()));
                }
                super.beforeHookedMethod(param);
            }
        });
    }

    public interface onVoiceFinishListener {
        void onPcmReadFinish();
    }
/*

    private void hookGetMinBuffer() {
        try {
            Method method = AudioRecord.class.getDeclaredMethod("getMinBufferSize", int.class, int.class, int.class);
            LogManager.getInstance().logError("method = " + method);
            XposedBridge.hookMethod(method, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    LogManager.getInstance().logInfo("hookGetMinBuffer ------------------");
                    LogManager.getInstance().logInfo("hookGetMinBuffer thisObject = " + param.thisObject + " name = " + param.method);
                    for (Object paramObj : param.args) {
                        LogManager.getInstance().logInfo("hookGetMinBuffer paramObj = " + paramObj + "   " + (paramObj == null ? " null " : param.getClass().getName()));
                        if (paramObj instanceof byte[]) {
                            LogManager.getInstance().logInfo("hookGetMinBuffer  paramObj instance byte[] length " + ((byte[]) paramObj).length);
                        }
                    }
                    LogManager.getInstance().logInfo("hookGetMinBuffer ------------------");
                }
            });
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }

    private void hookAudioTract() {
        XposedBridge.hookAllConstructors(AudioTrack.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                LogManager.getInstance().logInfo("hookAudioTract ------------------");
                LogManager.getInstance().logInfo("hookAudioTract thisObject = " + param.thisObject + " name = " + param.method);

                for (Object paramObj : param.args) {
                    LogManager.getInstance().logInfo("hookAudioTract paramObj = " + paramObj + "   " + (paramObj == null ? " null " : param.getClass().getName()));
                }
                LogManager.getInstance().logInfo("hookAudioTract ------------------");
                super.beforeHookedMethod(param);
            }
        });
    }


    private void hookWxDecoder(XC_LoadPackage.LoadPackageParam loadPackageParam) {
        Class wxRecoder = XposedHelpers.findClass("com.tencent.mm.modelvoice.MediaRecorder", loadPackageParam.classLoader);
        LogManager.getInstance().logInfo("hookDecoder wxRecoder = " + wxRecoder);
        Method[] declaredMethods = wxRecoder.getDeclaredMethods();
        for (Method method : declaredMethods) {
            XposedBridge.hookMethod(method, new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    super.afterHookedMethod(param);
                    LogManager.getInstance().logInfo("hookDecoder ------------------");
                    LogManager.getInstance().logInfo("hookDecoder thisObject = " + param.thisObject + " name = " + param.method + "    getResult =   " + param.getResult());
                    for (Object paramObj : param.args) {
                        if (paramObj instanceof short[]) {
                            LogManager.getInstance().logInfo("hookDecoder  paramObj instance short[] " + (((short[]) paramObj).length));
                        } else if (paramObj instanceof byte[]) {
                            LogManager.getInstance().logInfo("hookDecoder  paramObj instance byte[] " + (((byte[]) paramObj).length));
                        } else {
                            LogManager.getInstance().logInfo("hookDecoder paramObj = " + paramObj);
                        }
                    }
                    LogManager.getInstance().logInfo("hookDecoder ------------------");
                }
            });
        }
    }


    private void AudioTrackWrite() {
        HookDeviceModule.hookAllMethodByName(AudioTrack.class, "write", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);

                LogManager.getInstance().logInfo("AudioTrack ------------------");
                LogManager.getInstance().logInfo("AudioTrack thisObject = " + param.thisObject + " name = " + param.method);

                for (Object paramObj : param.args) {
                    LogManager.getInstance().logInfo("AudioTrack paramObj = " + paramObj + "   " + (paramObj == null ? " null " : param.getClass().getName()));
                    if (paramObj instanceof byte[]) {
                        LogManager.getInstance().logInfo("AudioTrack  paramObj instance byte[] length " + ((byte[]) paramObj).length);
                    }
                }
                LogManager.getInstance().logInfo("AudioTrack ------------------");
            }
        });

    }

*/

}
